#include "kore2.h"

/**
 * @brief Creates a new mixer instance
 *
 * The constructor creates a new mixer instance. It should never be called
 * directly, but rather using the Mixer::instance() method.
 */
kore::sound::Mixer::Mixer()
{
    printf("[mixer] Initializing...\n");

	// Clear all channels
	for(int n = 0; n < kore::sound::MAX_CHANNELS; this->m_channels[n++] = NULL);

    // load support for the OGG and MOD sample/music formats
    printf("[mixer] Opening audio - ch:2 sr:22050 cs:2048\n");
    if (Mix_OpenAudio( 22050, AUDIO_S16 , 2, 2048 ) != 0)
    {
        printf("[mixer] Error: Failed to open audio!\n");
    }
    else
    {
        printf("[mixer] Audio initialized Ok.\n");
    }

}

/**
 * @brief Destroys the mixer instance
 *
 * Cleans up all lose ends and closes the mixer
 */
kore::sound::Mixer::~Mixer()
{
	for(int n = 0; n < kore::sound::MAX_CHANNELS; n++)
	{
		if (this->m_channels[n]) delete this->m_channels[n];
	}
    Mix_CloseAudio();
    printf("[mixer] Terminating...\n");
}

/**
 * @brief Returns a mixer channel from the mixer
 *
 * We remove the index of the first channel since it's really a negative number.
 * This 0-aligns our channel list
 *
 * @param EChannels The channel to access
 * @return kore::sound::MixerChannel*
 */
kore::sound::MixerChannel* kore::sound::Mixer::getMixerChannel(kore::sound::EChannels channel)
{
	// if between first and last sound channel, including music channel.
	if (!this->m_channels[channel - kore::sound::FIRST_CHANNEL])
	{
		this->m_channels[channel - kore::sound::FIRST_CHANNEL] = new kore::sound::MixerChannel(channel);
	}
    return this->m_channels[channel - kore::sound::FIRST_CHANNEL];

	// TODO: Handle groups
}

/**
 * @brief MixerChannel Constructor
 *
 * ...
 *
 * @param EChannels channel
 */
kore::sound::MixerChannel::MixerChannel(kore::sound::EChannels channel)
: m_music(NULL)
, m_sound(NULL)
, m_group(0)
{
    this->m_channel = channel;
}

/**
 * @brief MixerChannel Destructor
 *
 * ...
 */
kore::sound::MixerChannel::~MixerChannel()
{
    if (this->m_music)
    {
        Mix_HaltMusic();
        Mix_FreeMusic(this->m_music);
    }
    if (this->m_sound)
    {
        // Mix_HaltChannel(); // Translate channels here
        Mix_FreeChunk(this->m_sound);
    }
}

/**
 * @brief Loads a music file for playback
 *
 * This method will load a file to be used as a music track. This can only be
 * performed on the CHAN_MUSIC channel.
 *
 * @param string The file to load
 * @return int 0 on success
 */
int kore::sound::MixerChannel::loadMusic(std::string file)
{
	// Operation is only available on the music channel
    if (this->m_channel != CHAN_MUSIC)
    {
        printf("[mixer] Warning - MixerChannel::loadMusic called on non-music channel\n");
        return -1;
    }
    if (this->m_music)
    {
        Mix_FreeMusic(this->m_music);
    }

    Mix_Music* mus = Mix_LoadMUS(file.c_str());
    this->m_music = mus;
}

/**
 * @brief Loads a music file for playback from a resource
 *
 * This method will load a file to be used as a music track. This can only be
 * performed on the CHAN_MUSIC channel. The file will be read from the specified
 * resource.
 *
 * @param kore::Resource The resource to load
 * @return int 0 on success
 */
int kore::sound::MixerChannel::loadMusic(::kore::Resource resource)
{
	// Operation is only available on the music channel
    if (this->m_channel != CHAN_MUSIC)
    {
        printf("[mixer] Warning - MixerChannel::loadMusic called on non-music channel\n");
        return -1;
    }
    if (this->m_music)
    {
        Mix_FreeMusic(this->m_music);
    }

    SDL_RWops* rwop;
    rwop = SDL_RWFromMem( resource.pdata, resource.len );
    Mix_Music* mus = Mix_LoadMUS_RW(rwop);
    this->m_music = mus;
}

/**
 * @brief Play a channel
 *
 * Will perform the appropriate action for the channel to ensure that the
 * proper soundtrack is playing etc.
 *
 * @param int If set to 1 playback will loop
 * @return int 0 on success
 */
int kore::sound::MixerChannel::play(int loop)
{
    if (this->m_channel == CHAN_MUSIC)
    {
        Mix_PlayMusic(this->m_music, loop);
		return 0;
    }
}

/**
 * @brief Returns the channel group of the channel
 *
 * For group channels this will return the actual group mapping!
 *
 * @return int The channel group
 */
int kore::sound::MixerChannel::getChannelGroup()
{
	return this->m_group;
}

/**
 * @brief Sets the channel group of the channel
 *
 * This method is only available to CHAN_SOUNDxx channels.
 *
 * @param int The group to assign to
 */
void kore::sound::MixerChannel::setChannelGroup(int group)
{
	// Basic  bounds checking
	if ((group >= 0) && (group <= MAX_GROUPS))
		this->m_group = group;
}